/*! \file    callfactory.h
	\author  HiddenSeeker

	Defines a call for all method
 */
#include "../semantics/classsemantic.h"
#include "../semantics/method.h"
#include <vector>
#pragma once

class ParentData;

class MethodSelectDelegate
{
public:
	 /*! This method is used for filling actions in constant table.
		 In default case it don't do anything
	  */
	 virtual void fillConstant(ParentData * data);
	 /*! This method is used for producing code.
		 In default case it don't do anything
	  */
	 virtual void produceCode(ParentData * data);
};


/*! Upcast delegate is used to provide upcasts
	Since upcasts are unchecked, we don't do things
 */
class UpcastDelegate : public MethodSelectDelegate
{
 private:
		 ClassSemantic * m_from;
		 ClassSemantic * m_to;
		 
 public:
	     inline UpcastDelegate(ClassSemantic * from, ClassSemantic * to)
		 {
			 m_from=from;
			 m_to=to;
		 }
};

/*! Downcast delegate is used to provide downcasts
	It uses check castto provide checked cast
 */
class DowncastDelegate: public MethodSelectDelegate
{
 private:
		 ClassSemantic * m_from;
		 ClassSemantic * m_to;

		 /*! Reference for class doing
		  */
		 unsigned short  m_class;
 public:
	     inline DowncastDelegate(ClassSemantic * from, ClassSemantic * to)
		 {
			 m_from=from;
			 m_to=to;
		 }
	     /*! This method is used for filling actions in constant table.
		     In default case it don't do anything
	      */
	     virtual void fillConstant(ParentData * data);
	     /*! This method is used for producing code.
		     In default case it don't do anything
	      */
	     virtual void produceCode(ParentData * data);
};


class InvokeCommon : public MethodSelectDelegate
{
 private:
		 ClassSemantic * m_me;
		 Method        * m_method;
		 Compound      * m_args;

		 unsigned short m_ref;
 public:
		inline InvokeCommon(ClassSemantic * me,Method * method,Compound * args)
		{
			m_me=me;
			m_method=method;
			m_args=args;
		}
		 /*! This method is used for filling actions in constant table.
		     In default case it don't do anything
	      */
	     virtual void fillConstant(ParentData * data);
	     /*! This method is used for producing code.
		     In default case it don't do anything
	      */
	     virtual void produceCode(ParentData * data);
};

class InvokePrecursor: public MethodSelectDelegate
{
 private:
		 ClassSemantic * m_me;
		 Method        * m_method;
		 Compound      * m_args;

		 unsigned short m_ref;
 public:
		inline InvokePrecursor(ClassSemantic * me,Method * method,Compound * args)
		{
			m_me=me;
			m_method=method;
			m_args=args;
		} 
		/*! This method is used for filling actions in constant table.
		     In default case it don't do anything
	      */
	     virtual void fillConstant(ParentData * data);
	     /*! This method is used for producing code.
		     In default case it don't do anything
	      */
	     virtual void produceCode(ParentData * data);
};

class InvokeInterface: public MethodSelectDelegate
{
 private:
		 ClassSemantic * m_me;
		 Method        * m_method;
		 Compound      * m_args;

		 unsigned short m_ref;
 public:
		inline InvokeInterface(ClassSemantic * me,Method * method,Compound * args)
		{
			m_me=me;
			m_method=method;
			m_args=args;
		} 
		/*! This method is used for filling actions in constant table.
		     In default case it don't do anything
	      */
	     virtual void fillConstant(ParentData * data);
	     /*! This method is used for producing code.
		     In default case it don't do anything
	      */
	     virtual void produceCode(ParentData * data);
};

class DoNothing : public MethodSelectDelegate
{ 
 public:
	  inline DoNothing()
	  {
	  }
};

/*! Describes a small delegate, which uses INVOKEVIRTUAL to perform simple manipulation with the data
 */
class SimpleInvocationDelegate: public MethodSelectDelegate
{
 private:
		 unsigned short m_methodref;
 protected:
	     /*! Must return a valid delegate reference
		  */
		 virtual unsigned short get(ConstantTable * table)=0;
 public:
		  /*! This method is used for filling actions in constant table.
			  In default case it don't do anything
	       */
	      virtual void fillConstant(ParentData * data);
	      /*! This method is used for producing code.
		      In default case it don't do anything
	       */
	      virtual void produceCode(ParentData * data);
};


class InvokeGetBoolean : public SimpleInvocationDelegate
{
 protected:
	     /*! Must return a valid delegate reference
		  */
		unsigned short get(ConstantTable * table);		
 public:
	    inline InvokeGetBoolean()
	    {
	    }
};

class CloneInteger: public SimpleInvocationDelegate
{
 protected:
	     /*! Must return a valid delegate reference
		  */
		unsigned short get(ConstantTable * table);
 public: 
	  inline CloneInteger()
	  {
	  }
};

class CloneReal: public SimpleInvocationDelegate
{
 protected:
	     /*! Must return a valid delegate reference
		  */
		unsigned short get(ConstantTable * table);
 public: 
	  inline CloneReal()
	  {
	  }
};

class CloneBoolean: public SimpleInvocationDelegate
{
 protected:
	     /*! Must return a valid delegate reference
		  */
		unsigned short get(ConstantTable * table);
 public: 
	  inline CloneBoolean()
	  {
	  }
};

class CloneCharacter: public SimpleInvocationDelegate
{
 protected:
	     /*! Must return a valid delegate reference
		  */
		unsigned short get(ConstantTable * table);
 public: 
	  inline CloneCharacter()
	  {
	  }
};

class CloneString : public SimpleInvocationDelegate
{
 protected:
	     /*! Must return a valid delegate reference
		  */
		unsigned short get(ConstantTable * table);
 public: 
	  inline CloneString()
	  {
	  }
};

/*! This delegate is used, when  we simple want to produce code from node
	and not want to hold it at other places
 */
class NodeCodeProducer: public MethodSelectDelegate
{
 private:
		 AbstractNode * m_node;
 public:
		inline NodeCodeProducer(AbstractNode * nd)
		{
			m_node=nd;
		}
		 /*! This method is used for filling actions in constant table.
		  */
	     virtual void fillConstant(ParentData * data);
	     /*! This method is used for producing code.
	      */
	     virtual void produceCode(ParentData * data);
};
/*! This delegate is used when we want to convert one object to other by creating other object
	and invoking special method, that fills a freshly created object with data from other object
	Commonly used by AssignmentCommand, FieldAssignment, ArrayAssignment to convert other operand
 */
class ObjectConverter: public MethodSelectDelegate
{
 private:
		 AbstractNode * m_node;
		 /*! Class, that must be created
		  */
		 ClassSemantic * m_class_data;
		 /*! Whether we should clone an argument before assignment
		  */
		 MethodSelectDelegate * m_clone;
		 /*! Invoked method to create from other argument
		  */
		 MethodSelectDelegate * m_invoke;
		 /*! Whether we should pop before exit
		  */
		 bool m_pop_before_exit;
		 /*! Class, that would be created
		  */
		 unsigned short m_class;
		 /*! Constructor of the class
		  */
		 unsigned short m_constructor;
 public:
		 /*! Creates a new object, that performs a conversion
			 \param[in] sArg           second argument, used for conversion
			 \param[in] _class         class of second argument, used to perform construction
			 \param[in] clone          whether we should clone other object before construction
			 \param[in] convertMethod  conversion method
			 \param[in] pop            whether we should pop before exit
		  */
		 inline ObjectConverter(AbstractNode * sArg,
								ClassSemantic * _class, 
								MethodSelectDelegate * clone,
								MethodSelectDelegate * convertMethod,
								bool pop)
		 {
			 m_node=sArg;
			 m_class_data=_class;
			 m_clone=clone;
			 m_invoke=convertMethod;
			 m_pop_before_exit=pop;
		 }
		 /*! This method is used for filling actions in constant table.
		  */
	     virtual void fillConstant(ParentData * data);
	     /*! This method is used for producing code.
	      */
	     virtual void produceCode(ParentData * data);
};
/*! This delegate is used to produce null reference, which we want to compare with other reference
	or other
 */
class NullReference: public MethodSelectDelegate
{
 private:
 public:
		inline NullReference()
		{
		}
		/*! This method is used for producing code.
	     */
	    virtual void produceCode(ParentData * data);
};
/*! This delegate performs reference checking, returning true if they are equal
 */
class CompareRefsEq: public MethodSelectDelegate
{
 private:
	    /*! A method ref to Convert::refsEq
		 */
		unsigned short m_method_ref;
 public:
		inline CompareRefsEq()
		{
		}
		/*! This method is used for filling actions in constant table.
		 */
	    virtual void fillConstant(ParentData * data);
	    /*! This method is used for producing code.
	     */
	    virtual void produceCode(ParentData * data);
};
/*! This delegate performs reference checking, returning false if they are equal
 */
class CompareRefsNeq: public MethodSelectDelegate
{
 private:
	    /*! A method ref to Convert::refsNeq
		 */
		unsigned short m_method_ref;
 public:
		inline CompareRefsNeq()
		{
		}
		/*! This method is used for filling actions in constant table.
		 */
	    virtual void fillConstant(ParentData * data);
	    /*! This method is used for producing code.
	     */
	    virtual void produceCode(ParentData * data);
};

class CallInformation;
/*! This delegates runs a function, when needed
	Used to run custom function, when comparing with operators ~,=,/=,/~
 */
class  CallMethodDelegate:public MethodSelectDelegate
{
 private:
		 CallInformation * m_info;
 public:
		 inline CallMethodDelegate(CallInformation * info)
		 {
			 m_info=info;
		 }
		 /*! This method is used for filling actions in constant table.
		  */
	     virtual void fillConstant(ParentData * data);
	     /*! This method is used for producing code.
	      */
	     virtual void produceCode(ParentData * data);
		 ~CallMethodDelegate();
};
class CallInformation
{
 public:
		MethodSelectDelegate *               Method;                 //! How we should invoke method call
		std::vector<std::vector<MethodSelectDelegate *>>  ArgumentConversions;    //! What we should do with arguments

		CallInformation()
		{
			Method=NULL;
		}

		/*! Fills a constant table
		 */
		inline void fillConstant(ParentData * data)
		{
			Method->fillConstant(data);
		    for (unsigned int i=0;i<ArgumentConversions.size();i++)
				for (unsigned int j=0;j<ArgumentConversions[i].size();j++)
					ArgumentConversions[i][j]->fillConstant(data);
		}
		/*! Produces an execution code
		 */
		inline void produceCode(ParentData * data)
		{
		    for (unsigned int i=0;i<ArgumentConversions.size();i++)
				for (unsigned int j=0;j<ArgumentConversions[i].size();j++)
					ArgumentConversions[i][j]->produceCode(data);
			Method->produceCode(data);	
		}

		~CallInformation();
		//We cannont produce code before computing argument, so code producing must be handled by invoking node

};


/*! Tests, whether node is void identifier
 */
bool isVoidIdentifier(AbstractNode * arg);

/*! Selects an invocation delegate by data
	\param[in] arg argument
	\param[in] method method data
	\param[in] sem    semantic of class
	\param[in] data   data used by argument
	\return argument
 */
MethodSelectDelegate * createInvocationDelegate(AbstractNode *arg,
												Method * method,
												ClassSemantic * sem,
												SemanticVisitorData *  data,
												Method * curMethod);

MethodSelectDelegate * selectArgumentConversion(AbstractNode *arg,const std::string & type,ParentData *  data,Method * curMethod,MethodSelectDelegate ** clone=NULL);

/*! 
	Defines a creation of call information for method 
	\param[in] firstArg argument class
	\param[in] fArgType first argument type
	\param[in] method (method, which is invoked)
	\param[in] compound compound data
	\param[in] data used data
	\return data
 */
CallInformation * createCallInformation(AbstractNode * firstArg,
										ClassSemantic * fArgType,
										Method * method,
										Compound * compound, 
										ParentData * data);

/*! Creates a cloning method for copy
	\param[in] type  type string
	\param[in] data   visitor data
 */
MethodSelectDelegate * createCloningMethod(std::string & type, SemanticVisitorData * data);

/*! Selects a conversion for assignment
	\param[in] fSem - type, to which it must be assigned
	\param[in] sSem - assigning type
	\param[in] data used data
	\param[in] sArg converter operand
	\param[in] ptr  used pointer
	\param[in] delegates used delegates
 */
void selectAssignmentConversion(ClassSemantic * fSem,
								ClassSemantic * sSem,
								ParentData * data,
								AbstractNode * sArg,
								AbstractNode * ptr,
								std::vector<MethodSelectDelegate *> & delegates
								);
